#include <stdlib.h>
#include <stdint.h>
#include <stdbool.h>
#include <ctype.h>
#include <string.h>

#include "demo_system.h"
#include "dev_access.h"
#include "gpio.h"
#include "uart.h"

extern void aes_sbox(uint32_t input_x[16],uint32_t output_s[16]);

// format conversion

int  xtoi( char x ) {
  if     ( ( x >= '0' ) && ( x <= '9' ) ) {
    return ( ( int )( x ) - ( int )( '0' )      );
  }
  else if( ( x >= 'a' ) && ( x <= 'f' ) ) {
    return ( ( int )( x ) - ( int )( 'a' ) + 10 );
  }
  else if( ( x >= 'A' ) && ( x <= 'F' ) ) {
    return ( ( int )( x ) - ( int )( 'A' ) + 10 );
  }

  return -1;
}

char itox( int  x ) {
  if     ( ( x >=  0 ) && ( x <=  9 ) ) {
    return ( char )( ( int )( '0' ) + x       );
  }
  else if( ( x >= 10 ) && ( x <= 15 ) ) {
    return ( char )( ( int )( 'A' ) + x - 10  );
  }

  return '?';
}

void xtob( uint8_t* r, char* x, int n ) {
  for( int i = 0; i < n; i++ ) {
    r[ i ]  = ( uint8_t )( ( xtoi( *x++ ) & 0xF ) << 4 );
    r[ i ] |= ( uint8_t )( ( xtoi( *x++ ) & 0xF ) << 0 );
  }
}

void btox( char* r, uint8_t* x, int n ) {
  for( int i = 0; i < n; i++ ) {
    *r++ = itox( ( uint8_t )( ( x[ i ] >> 4 ) & 0xF ) );
    *r++ = itox( ( uint8_t )( ( x[ i ] >> 0 ) & 0xF ) );
  }

  *r++ = '\x00';
}


// I/O via UART: raw

void    uart_wr  ( uart_t uart, uint8_t x ) {
  while( DEV_READ( uart + UART_STATUS_REG ) & UART_STATUS_TX_FULL  ) {
    /* spin */
  }

         DEV_WRITE( uart + UART_TX_REG, x );
}

uint8_t uart_rd  ( uart_t uart            ) {
  while( DEV_READ( uart + UART_STATUS_REG ) & UART_STATUS_RX_EMPTY ) { 
    /* spin */
  }

  return DEV_READ ( uart + UART_RX_REG    );
}


// I/O via UART: structured

void octetstr_wr( uart_t uart, const uint8_t* x, int n_x ) {
             uart_wr( uart, itox( ( n_x      >> 4 ) & 0xF ) );
             uart_wr( uart, itox( ( n_x      >> 0 ) & 0xF ) );

             uart_wr( uart, ':'    );

  for( int i = 0; i < n_x; i++ ) {
             uart_wr( uart, itox( (   x[ i ] >> 4 ) & 0xF ) );
             uart_wr( uart, itox( (   x[ i ] >> 0 ) & 0xF ) );
  }

             uart_wr( uart, '\x0D' );
}

int  octetstr_rd( uart_t uart,       uint8_t* r, int n_r ) {
  int n  = ( ( xtoi( uart_rd( uart ) ) & 0xF ) << 4 );
  
      n |= ( ( xtoi( uart_rd( uart ) ) & 0xF ) << 0 );

  if( n > n_r ) {
    return 0xF1000000 |              ( n << 0 );
  }

  uint8_t t;

  if( ( t = uart_rd( uart ) ) != ':' ) {
    return 0xF2000000 | ( t << 8 ) | ( n << 0 );
  }

  int i = 0;

  for( i = 0; i < n; i++ ) {
    r[ i ] = 0;

    if( ( ( t = uart_rd( uart ) ) == '\x0D' ) || !isxdigit( t ) ) {
      break;
    }
    r[ i ] |= ( uint8_t )( xtoi( t ) & 0xF ) << 4;

    if( ( ( t = uart_rd( uart ) ) == '\x0D' ) || !isxdigit( t ) ) {
      break;
    }
    r[ i ] |= ( uint8_t )( xtoi( t ) & 0xF ) << 0;
  }

  while( t != '\x0D' ) {
    t = uart_rd( uart );
  }

  return i;
}

void ack_wr( uart_t uart, bool x ) {
  if( x ) {
    uart_wr( uart, '+' ); uart_wr( uart, '\x0D' );
  }
  else {
    uart_wr( uart, '-' ); uart_wr( uart, '\x0D' );
  }
}

bool ack_rd( uart_t uart         ) {
  uint8_t x = uart_rd( uart );
  uint8_t t = uart_rd( uart );

  while( t != '\x0D' ) {
    t = uart_rd( uart );
  }

  return ( x == '+' ) ? ( true ) : ( false );
}


// main function 

int main( int argc, char* argv[] ) {
  set_global_interrupt_enable( 0 );

  while(1){
    uint8_t temp[4];
    uart_t uart = DEFAULT_UART;

    //input x0
    uint32_t x0;
    octetstr_rd( uart, temp, 4 );
    x0  = (uint32_t) temp[0] << 24;
    x0 |= (uint32_t) temp[1] << 16;
    x0 |= (uint32_t) temp[2] <<  8;
    x0 |= (uint32_t) temp[3]      ;
    // puts("The x0 is:\n");
    // puthex(x0);
    // puts("\n");

    //input x1
    uint32_t x1;
    octetstr_rd( uart, temp, 4 );
    x1  = (uint32_t) temp[0] << 24;
    x1 |= (uint32_t) temp[1] << 16;
    x1 |= (uint32_t) temp[2] <<  8;
    x1 |= (uint32_t) temp[3]      ;
    // puts("The x1 is:\n");
    // puthex(x1);
    // puts("\n");

    //input x2
    uint32_t x2;
    octetstr_rd( uart, temp, 4 );
    x2  = (uint32_t) temp[0] << 24;
    x2 |= (uint32_t) temp[1] << 16;
    x2 |= (uint32_t) temp[2] <<  8;
    x2 |= (uint32_t) temp[3]      ;
    // puts("The x2 is:\n");
    // puthex(x2);
    // puts("\n");

    //input x3
    uint32_t x3;
    octetstr_rd( uart, temp, 4 );
    x3  = (uint32_t) temp[0] << 24;
    x3 |= (uint32_t) temp[1] << 16;
    x3 |= (uint32_t) temp[2] <<  8;
    x3 |= (uint32_t) temp[3]      ;
    // puts("The x3 is:\n");
    // puthex(x3);
    // puts("\n");

    //input x4
    uint32_t x4;
    octetstr_rd( uart, temp, 4 );
    x4  = (uint32_t) temp[0] << 24;
    x4 |= (uint32_t) temp[1] << 16;
    x4 |= (uint32_t) temp[2] <<  8;
    x4 |= (uint32_t) temp[3]      ;
    // puts("The x4 is:\n");
    // puthex(x4);
    // puts("\n");

    //input x5
    uint32_t x5;
    octetstr_rd( uart, temp, 4 );
    x5  = (uint32_t) temp[0] << 24;
    x5 |= (uint32_t) temp[1] << 16;
    x5 |= (uint32_t) temp[2] <<  8;
    x5 |= (uint32_t) temp[3]      ;
    // puts("The x5 is:\n");
    // puthex(x5);
    // puts("\n");

    //input x6
    uint32_t x6;
    octetstr_rd( uart, temp, 4 );
    x6  = (uint32_t) temp[0] << 24;
    x6 |= (uint32_t) temp[1] << 16;
    x6 |= (uint32_t) temp[2] <<  8;
    x6 |= (uint32_t) temp[3]      ;
    // puts("The x6 is:\n");
    // puthex(x6);
    // puts("\n");

    //input x7
    uint32_t x7;
    octetstr_rd( uart, temp, 4 );
    x7  = (uint32_t) temp[0] << 24;
    x7 |= (uint32_t) temp[1] << 16;
    x7 |= (uint32_t) temp[2] <<  8;
    x7 |= (uint32_t) temp[3]      ;
    // puts("The x7 is:\n");
    // puthex(x7);
    // puts("\n");

    //input y0
    uint32_t y0;
    octetstr_rd( uart, temp, 4 );
    y0  = (uint32_t) temp[0] << 24;
    y0 |= (uint32_t) temp[1] << 16;
    y0 |= (uint32_t) temp[2] <<  8;
    y0 |= (uint32_t) temp[3]      ;
    // puts("The y0 is:\n");
    // puthex(y0);
    // puts("\n");

    //input y1
    uint32_t y1;
    octetstr_rd( uart, temp, 4 );
    y1  = (uint32_t) temp[0] << 24;
    y1 |= (uint32_t) temp[1] << 16;
    y1 |= (uint32_t) temp[2] <<  8;
    y1 |= (uint32_t) temp[3]      ;
    // puts("The y1 is:\n");
    // puthex(y1);
    // puts("\n");

    //input y2
    uint32_t y2;
    octetstr_rd( uart, temp, 4 );
    y2  = (uint32_t) temp[0] << 24;
    y2 |= (uint32_t) temp[1] << 16;
    y2 |= (uint32_t) temp[2] <<  8;
    y2 |= (uint32_t) temp[3]      ;
    // puts("The y2 is:\n");
    // puthex(y2);
    // puts("\n");

    //input y3
    uint32_t y3;
    octetstr_rd( uart, temp, 4 );
    y3  = (uint32_t) temp[0] << 24;
    y3 |= (uint32_t) temp[1] << 16;
    y3 |= (uint32_t) temp[2] <<  8;
    y3 |= (uint32_t) temp[3]      ;
    // puts("The y3 is:\n");
    // puthex(y3);
    // puts("\n");

    //input y4
    uint32_t y4;
    octetstr_rd( uart, temp, 4 );
    y4  = (uint32_t) temp[0] << 24;
    y4 |= (uint32_t) temp[1] << 16;
    y4 |= (uint32_t) temp[2] <<  8;
    y4 |= (uint32_t) temp[3]      ;
    // puts("The y4 is:\n");
    // puthex(y4);
    // puts("\n");

    //input y5
    uint32_t y5;
    octetstr_rd( uart, temp, 4 );
    y5  = (uint32_t) temp[0] << 24;
    y5 |= (uint32_t) temp[1] << 16;
    y5 |= (uint32_t) temp[2] <<  8;
    y5 |= (uint32_t) temp[3]      ;
    // puts("The y5 is:\n");
    // puthex(y5);
    // puts("\n");

    //input y6
    uint32_t y6;
    octetstr_rd( uart, temp, 4 );
    y6  = (uint32_t) temp[0] << 24;
    y6 |= (uint32_t) temp[1] << 16;
    y6 |= (uint32_t) temp[2] <<  8;
    y6 |= (uint32_t) temp[3]      ;
    // puts("The y6 is:\n");
    // puthex(y6);
    // puts("\n");

    //input y7
    uint32_t y7;
    octetstr_rd( uart, temp, 4 );
    y7  = (uint32_t) temp[0] << 24;
    y7 |= (uint32_t) temp[1] << 16;
    y7 |= (uint32_t) temp[2] <<  8;
    y7 |= (uint32_t) temp[3]      ;
    // puts("The y7 is:\n");
    // puthex(y7);
    // puts("\n");

    uint32_t input_x[16];
    uint32_t output_s[16];
    uint32_t back;

    asm volatile(
      ".include \"ise.S\"\n\t"
      "sw        t6, %[t6b]\n\t"
      "lw        t6, %[srcx0]\n\t"
      "cbm.or    t6, t6, x0, 0\n\t"
      "cbm.or    t6, t6, x0, 3\n\t"
      "sw        t6, %[resx0]\n\t"
      "nop\n\t"
      "nop\n\t"
      "nop\n\t"
      "nop\n\t"
      "lw        t6, %[srcx1]\n\t"
      "cbm.or    t6, t6, x0, 0\n\t"
      "cbm.or    t6, t6, x0, 3\n\t"
      "sw        t6, %[resx1]\n\t"
      "nop\n\t"
      "nop\n\t"
      "nop\n\t"
      "nop\n\t"
      "lw        t6, %[srcx2]\n\t"
      "cbm.or    t6, t6, x0, 0\n\t"
      "cbm.or    t6, t6, x0, 3\n\t"
      "sw        t6, %[resx2]\n\t"
      "nop\n\t"
      "nop\n\t"
      "nop\n\t"
      "nop\n\t"
      "lw        t6, %[srcx3]\n\t"
      "cbm.or    t6, t6, x0, 0\n\t"
      "cbm.or    t6, t6, x0, 3\n\t"
      "sw        t6, %[resx3]\n\t"
      "nop\n\t"
      "nop\n\t"
      "nop\n\t"
      "nop\n\t"
      "lw        t6, %[srcx4]\n\t"
      "cbm.or    t6, t6, x0, 0\n\t"
      "cbm.or    t6, t6, x0, 3\n\t"
      "sw        t6, %[resx4]\n\t"
      "nop\n\t"
      "nop\n\t"
      "nop\n\t"
      "nop\n\t"
      "lw        t6, %[srcx5]\n\t"
      "cbm.or    t6, t6, x0, 0\n\t"
      "cbm.or    t6, t6, x0, 3\n\t"
      "sw        t6, %[resx5]\n\t"
      "nop\n\t"
      "nop\n\t"
      "nop\n\t"
      "nop\n\t"
      "lw        t6, %[srcx6]\n\t"
      "cbm.or    t6, t6, x0, 0\n\t"
      "cbm.or    t6, t6, x0, 3\n\t"
      "sw        t6, %[resx6]\n\t"
      "nop\n\t"
      "nop\n\t"
      "nop\n\t"
      "nop\n\t"
      "lw        t6, %[srcx7]\n\t"
      "cbm.or    t6, t6, x0, 0\n\t"
      "cbm.or    t6, t6, x0, 3\n\t"
      "sw        t6, %[resx7]\n\t"
      "nop\n\t"
      "nop\n\t"
      "nop\n\t"
      "nop\n\t"
      "lw        t6, %[srcy0]\n\t"
      "cbm.or    t6, t6, x0, 0\n\t"
      "cbm.or    t6, t6, x0, 3\n\t"
      "sw        t6, %[resy0]\n\t"
      "nop\n\t"
      "nop\n\t"
      "nop\n\t"
      "nop\n\t"
      "lw        t6, %[srcy1]\n\t"
      "cbm.or    t6, t6, x0, 0\n\t"
      "cbm.or    t6, t6, x0, 3\n\t"
      "sw        t6, %[resy1]\n\t"
      "nop\n\t"
      "nop\n\t"
      "nop\n\t"
      "nop\n\t"
      "lw        t6, %[srcy2]\n\t"
      "cbm.or    t6, t6, x0, 0\n\t"
      "cbm.or    t6, t6, x0, 3\n\t"
      "sw        t6, %[resy2]\n\t"
      "nop\n\t"
      "nop\n\t"
      "nop\n\t"
      "nop\n\t"
      "lw        t6, %[srcy3]\n\t"
      "cbm.or    t6, t6, x0, 0\n\t"
      "cbm.or    t6, t6, x0, 3\n\t"
      "sw        t6, %[resy3]\n\t"
      "nop\n\t"
      "nop\n\t"
      "nop\n\t"
      "nop\n\t"
      "lw        t6, %[srcy4]\n\t"
      "cbm.or    t6, t6, x0, 0\n\t"
      "cbm.or    t6, t6, x0, 3\n\t"
      "sw        t6, %[resy4]\n\t"
      "nop\n\t"
      "nop\n\t"
      "nop\n\t"
      "nop\n\t"
      :[t6b]"=m"(back),[srcx0]"=m"(x0),[srcx1]"=m"(x1),[srcx2]"=m"(x2),[srcx3]"=m"(x3),[srcx4]"=m"(x4),[srcx5]"=m"(x5),[srcx6]"=m"(x6),[srcx7]"=m"(x7),[srcy0]"=m"(y0),[srcy1]"=m"(y1),[srcy2]"=m"(y2),[srcy3]"=m"(y3),[srcy4]"=m"(y4)
      :[resx0]"m"(input_x[0]),[resx1]"m"(input_x[1]),[resx2]"m"(input_x[2]),[resx3]"m"(input_x[3]),[resx4]"m"(input_x[4]),[resx5]"m"(input_x[5]),[resx6]"m"(input_x[6]),[resx7]"m"(input_x[7]),[resy0]"m"(input_x[8]),[resy1]"m"(input_x[9]),[resy2]"m"(input_x[10]),[resy3]"m"(input_x[11]),[resy4]"m"(input_x[12])
    );

    asm volatile(
      "lw        t6, %[srcy5]\n\t"
      "cbm.or    t6, t6, x0, 0\n\t"
      "cbm.or    t6, t6, x0, 3\n\t"
      "sw        t6, %[resy5]\n\t"
      "nop\n\t"
      "nop\n\t"
      "nop\n\t"
      "nop\n\t"
      "lw        t6, %[srcy6]\n\t"
      "cbm.or    t6, t6, x0, 0\n\t"
      "cbm.or    t6, t6, x0, 3\n\t"
      "sw        t6, %[resy6]\n\t"
      "nop\n\t"
      "nop\n\t"
      "nop\n\t"
      "nop\n\t"
      "lw        t6, %[srcy7]\n\t"
      "cbm.or    t6, t6, x0, 0\n\t"
      "cbm.or    t6, t6, x0, 3\n\t"
      "sw        t6, %[resy7]\n\t"
      "lw        t6, %[t6b]\n\t"
      "nop\n\t"
      "nop\n\t"
      "nop\n\t"
      "nop\n\t"
      :[t6b]"=m"(back),[srcy5]"=m"(y5),[srcy6]"=m"(y6),[srcy7]"=m"(y7)
      :[resy5]"m"(input_x[13]),[resy6]"m"(input_x[14]),[resy7]"m"(input_x[15])
    );

    DEV_WRITE(GPIO_OUT, 0x1);
    aes_sbox(input_x,output_s);
    DEV_WRITE(GPIO_OUT, 0x0);

    // asm volatile(
    //   "nop\n\t"
    //   "nop\n\t"
    //   "nop\n\t"
    //   "nop\n\t"
    //   "nop\n\t"
    //   "nop\n\t"
    //   "nop\n\t"
    //   "nop\n\t"
    //   "nop\n\t"
    //   "nop\n\t"
    //   "nop\n\t"
    //   "nop\n\t"
    //   "nop\n\t"
    //   "nop\n\t"
    //   "nop\n\t"
    //   "nop\n\t"
    //   "nop\n\t"
    //   "nop\n\t"
    //   "nop\n\t"
    //   "nop\n\t"
    //   "nop\n\t"
    //   "nop\n\t"
    //   "nop\n\t"
    //   "nop\n\t"
    //   "sw        t6, %[t6b]\n\t"
    //   "lw        t6, %[data0]\n\t"
    //   "cbm.or    t6, t6, x0, 0\n\t"
    //   "sw        t6, %[data0]\n\t"
    //   "lw        t6, %[data1]\n\t"
    //   "cbm.or    t6, t6, x0, 0\n\t"
    //   "sw        t6, %[data1]\n\t"
    //   "lw        t6, %[data2]\n\t"
    //   "cbm.or    t6, t6, x0, 0\n\t"
    //   "sw        t6, %[data2]\n\t"
    //   "lw        t6, %[data3]\n\t"
    //   "cbm.or    t6, t6, x0, 0\n\t"
    //   "sw        t6, %[data3]\n\t"
    //   "lw        t6, %[data4]\n\t"
    //   "cbm.or    t6, t6, x0, 0\n\t"
    //   "sw        t6, %[data4]\n\t"
    //   "lw        t6, %[data5]\n\t"
    //   "cbm.or    t6, t6, x0, 0\n\t"
    //   "sw        t6, %[data5]\n\t"
    //   "lw        t6, %[data6]\n\t"
    //   "cbm.or    t6, t6, x0, 0\n\t"
    //   "sw        t6, %[data6]\n\t"
    //   "lw        t6, %[data7]\n\t"
    //   "cbm.or    t6, t6, x0, 0\n\t"
    //   "sw        t6, %[data7]\n\t"
    //   "lw        t6, %[data8]\n\t"
    //   "cbm.or    t6, t6, x0, 0\n\t"
    //   "sw        t6, %[data8]\n\t"
    //   "lw        t6, %[data9]\n\t"
    //   "cbm.or    t6, t6, x0, 0\n\t"
    //   "sw        t6, %[data9]\n\t"
    //   "lw        t6, %[data10]\n\t"
    //   "cbm.or    t6, t6, x0, 0\n\t"
    //   "sw        t6, %[data10]\n\t"
    //   "lw        t6, %[data11]\n\t"
    //   "cbm.or    t6, t6, x0, 0\n\t"
    //   "sw        t6, %[data11]\n\t"
    //   "lw        t6, %[data12]\n\t"
    //   "cbm.or    t6, t6, x0, 0\n\t"
    //   "sw        t6, %[data12]\n\t"
    //   "lw        t6, %[data13]\n\t"
    //   "cbm.or    t6, t6, x0, 0\n\t"
    //   "sw        t6, %[data13]\n\t"
    //   "lw        t6, %[data14]\n\t"
    //   "cbm.or    t6, t6, x0, 0\n\t"
    //   "sw        t6, %[data14]\n\t"
    //   "lw        t6, %[data15]\n\t"
    //   "cbm.or    t6, t6, x0, 0\n\t"
    //   "sw        t6, %[data15]\n\t"
    //   "lw        t6, %[t6b]\n\t"
    //   :[t6b]"=m"(back),[data0]"=m"(output_s[0]),[data1]"=m"(output_s[1]),[data2]"=m"(output_s[2]),[data3]"=m"(output_s[3]),[data4]"=m"(output_s[4]),[data5]"=m"(output_s[5]),[data6]"=m"(output_s[6]),[data7]"=m"(output_s[7]),[data8]"=m"(output_s[8]),[data9]"=m"(output_s[9]),[data10]"=m"(output_s[10]),[data11]"=m"(output_s[11]),[data12]"=m"(output_s[12]),[data13]"=m"(output_s[13]),[data14]"=m"(output_s[14]),[data15]"=m"(output_s[15])
    // );

    // puthex(output_s[0]);
    // puts("\n");
    // puthex(output_s[8]);
    // puts("\n");
    // puts("\n");
    // puthex(output_s[1]);
    // puts("\n");
    // puthex(output_s[9]);
    // puts("\n");
    // puts("\n");
    // puthex(output_s[2]);
    // puts("\n");
    // puthex(output_s[10]);
    // puts("\n");
    // puts("\n");
    // puthex(output_s[3]);
    // puts("\n");
    // puthex(output_s[11]);
    // puts("\n");
    // puts("\n");
    // puthex(output_s[4]);
    // puts("\n");
    // puthex(output_s[12]);
    // puts("\n");
    // puts("\n");
    // puthex(output_s[5]);
    // puts("\n");
    // puthex(output_s[13]);
    // puts("\n");
    // puts("\n");
    // puthex(output_s[6]);
    // puts("\n");
    // puthex(output_s[14]);
    // puts("\n");
    // puts("\n");
    // puthex(output_s[7]);
    // puts("\n");
    // puthex(output_s[15]);
    // puts("\n");
  }
  return 0;
}
